import { Text } from '@tarojs/components'
/**
 * 深拷贝
 * @param objects 需要拷贝的数据
 * @returns 深克隆的数据
 */
export const DeepClone = (data: any = {}) => {
    //string,number,bool,null,undefined,symbol
    //object,array,date
    if (data && typeof data === "object") {
        //针对函数的拷贝
        if (typeof data === "function") {
            let tempFunc = data.bind(null)
            tempFunc.prototype = DeepClone(data.prototype)
            return tempFunc
        }

        switch (Object.prototype.toString.call(data)) {
            case "[object String]": return data.toString()
            case "[object Number]": return Number(data.toString())
            case "[object Boolean]": return new Boolean(data.toString())
            case "[object Date]": return new Date(data.getTime())
            case "[object Array]":
                let arr: any = []
                for (let i = 0; i < data.length; i++) {
                    arr[i] = DeepClone(data[i])
                }
                return arr

            //js自带对象或用户自定义类实例
            case "[object Object]":
                let obj = {}
                for (let key in data) {
                    //会遍历原型链上的属性方法，可以用hasOwnProperty来控制 （obj.hasOwnProperty(prop)
                    obj[key] = DeepClone(data[key])
                }
                return obj
        }
    } else {
        //string,number,bool,null,undefined,symbol
        return data
    }
}

/** 
 * 计算对象id，只适用于选择对象组件
 */
export const executeId=(obj):string => { 
    return obj.userId + obj.userName || obj.groupId + obj.groupName
}

/**
 * 判断当前是否为数组类型，且不为空
 * @param arr 需要判断为数组的数据
 * @returns boolean 
 */
export const getIsMapArray = (arr) => {
    return Array.isArray(arr) && arr.length > 0
}

/**
 * 从富文本中提取图片地址数组
 * @param content 富文本内容
 * @returns url数组
 */
export const useGetImgInRichText = (content) => {
    if (!content) {
        return
    }
    let imgs = content.match(/<img[^>]+>/g);
    if (!imgs) {
        return
    }
    let arrImg: any = []
    for (let j = 0; j < imgs.length; j++) {
        imgs[j].replace(/<img[^>]*src=['"]([^'"]+)[^>]*>/gi, function (match, capture) {
            arrImg.push(capture)
        })
    }
    return arrImg
}


/**
 * 将对象数组转为对象 
 * @param arr 对象数组，数组最少需要两个属性
 * @param attr1 对象key值取值
 * @param attr2 对象value取值
 * @returns obj
 */
export const useArrayToObject = (arr, attr1: string, attr2: string) => {
    let obj = {}

    if (!getIsMapArray(arr)) {
        return {}
    }

    arr.forEach(item => {
        let key1 = item[attr1]
        obj[key1] = item[attr2]
    })

    return obj
}

/**
 * 比较两个对象数组key对应的值是否有相同的
 * @param arr1 对象数组1
 * @param arr2 对象数组2
 * @param key  比较的key值
 * @returns boolean
 */
export const useCompareArray = (arr1, arr2, key) => {

    if (!getIsMapArray(arr1) || !getIsMapArray(arr2)) {
        return false
    }

    let flag = false

    for (let i = 0; i < arr1.length; i++) {
        const item = arr1[i];
        for (let j = 0; j < arr2.length; j++) {
            const child = arr2[j];
            if (item[key] === child[key]) {
                flag = true
            }
        }
    }

    return flag
}

/**
 * 对象数组中是否有和该对象键值相同的项
 * @param arr 对象数组
 * @param item 对象
 * @param key 键值
 * @returns boolean true代表对象数组包含这个对象
 */
export const arraySome = (arr, item, key: string):boolean => {
    if (!getIsMapArray(arr)) {
        return false
    }

    return arr.some(child => child[key] === item[key])
}

/**
 * 对象数组中是否有和该对象键值相同的项，对比两个属性
 * @param arr 对象数组
 * @param item 对象
 * @param key1 键值1
 * @param key2 键值2
 * @returns boolean true代表对象数组包含这个对象
 */
export const arraySomeDouble = (arr, item, key1: string,key2:string):boolean => {
    if (!getIsMapArray(arr)) {
        return false
    }

    return arr.some(
      (child) =>
        child[key1] === item[key1] &&item[key2] === child[key2]
    );
}

/**
 * 删除arr中的item对象
 * @param item 对象
 * @param arr 对象数组
 * @param key 通过哪个关键词来判断
 * @returns 删除item的array
 */
export const delItemFromArray = (item, arr, key: string) => {
    if (!getIsMapArray(arr)) {
        return arr
    }

    let array: any = []
    arr.forEach((child, index) => {
        if (child[key] != item[key]) {
            array.push(child)
        }
    })


    return array
}

/**
 * 合并两个对象数组并去重
 * @param arr1 对象数组1
 * @param arr2 对象数组2
 * @param key 比较的键属性
 * @returns 合并去重后的数组
 */
export const concatArray = (arr1, arr2, key) => {


    if (!Array.isArray(arr1) || !Array.isArray(arr2)) {
        return []
    }
    let json: object[] = arr1.concat(arr2); //两个数组对象合并

    if (!key) {
        return json
    }

    let newJson: any = []; //盛放去重后数据的新数组
    for (let item1 of json) {  //循环json数组对象的内容
        let flag = true;  //建立标记，判断数据是否重复，true为不重复
        for (let item2 of newJson) {  //循环新数组的内容
            if (item1[key] == item2[key]) { //让json数组对象的内容与新数组的内容作比较，相同的话，改变标记为false
                flag = false;
            }
        }
        if (flag && item1[key]) { //判断是否重复
            newJson.push(item1); //不重复的放入新数组。  新数组的内容会继续进行上边的循环。
        }
    }

    return newJson
}


/**
 * 去除arr1中与arr2相同的项
 * @param arr1 主数组
 * @param arr2 子数组
 * @param key 对比的键值
 * @returns 去处重叠项的arr1
 */
export const removeArrayChild = (arr1, arr2, key) => {
    if (!getIsMapArray(arr1) || !getIsMapArray(arr2)) {
        return []
    }

    let newArr: object[] = []
    arr1.forEach((item) => {
        if (!arraySome(arr2, item, key)) {
            newArr.push(item)
        }
    })

    return newArr
}

/**
 * 修改对象数组中一项的值
 * @param arr 对象数组
 * @param item 对象数组中的某一项
 * @param key 对象的的key
 * @compareKey 对照判断的key
 * @param value 修改的value
 * @returns 修改了item这项key对应的值返回新数组
 */
export const editArrayItem = (arr, item, key, value, compareKey = key) => {
    if (!getIsMapArray(arr)) {
        return arr
    }

    let list = DeepClone(arr)
    list.forEach((child) => {
        if (child[compareKey] === item[compareKey]) {
            child[key] = value
        }
    })

    return list
}

/**
 * 
 * @param source 源字符串
 * @param keyword 关键词
 * @returns 新字符串
 */
export const heightLight = (source, keyword) => {
    const reg = new RegExp(keyword, 'gi')
    return source.replace(reg, `<rich-text style="color: #0099ff;display:inline-block">${keyword}</rich-text>`)
}

/**
 * 获取对象数组的选中属性 0未选择 1选择 2半选
 * @param arr1 对象数组
 * @param key 用于判断是否选中的属性
 * @returns 0/1/2
 */
export const getCheckValue = (arr1, key) => {
    if (!getIsMapArray(arr1)) {
        return 0
    }

    let flag = 0
    if (arr1.every(i => i[key])) {
        flag = 1
    } else if (arr1.some(i => i[key])) {
        flag = 2
    }

    return flag
}



// import root from './.internal/root.js'
/**
 * Checks if `value` is the
 * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
 * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
 *
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an object, else `false`.
 * @example
 *
 * isObject({})
 * // => true
 *
 * isObject([1, 2, 3])
 * // => true
 *
 * isObject(Function)
 * // => true
 *
 * isObject(null)
 * // => false
 */

function isObject(value) {
    const type = typeof value
    return value != null && (type === 'object' || type === 'function')
}

/**
 * Creates a debounced function that delays invoking `func` until after `wait`
 * milliseconds have elapsed since the last time the debounced function was
 * invoked, or until the next browser frame is drawn. The debounced function
 * comes with a `cancel` method to cancel delayed `func` invocations and a
 * `flush` method to immediately invoke them. Provide `options` to indicate
 * whether `func` should be invoked on the leading and/or trailing edge of the
 * `wait` timeout. The `func` is invoked with the last arguments provided to the
 * debounced function. Subsequent calls to the debounced function return the
 * result of the last `func` invocation.
 *
 * **Note:** If `leading` and `trailing` options are `true`, `func` is
 * invoked on the trailing edge of the timeout only if the debounced function
 * is invoked more than once during the `wait` timeout.
 *
 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
 * until the next tick, similar to `setTimeout` with a timeout of `0`.
 *
 * If `wait` is omitted in an environment with `requestAnimationFrame`, `func`
 * invocation will be deferred until the next frame is drawn (typically about
 * 16ms).
 *
 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
 * for details over the differences between `debounce` and `throttle`.
 *
 * @since 0.1.0
 * @category Function
 * @param {Function} func The function to debounce.
 * @param {number} [wait=0]
 *  The number of milliseconds to delay; if omitted, `requestAnimationFrame` is
 *  used (if available).
 * @param {Object} [options={}] The options object.
 * @param {boolean} [options.leading=false]
 *  Specify invoking on the leading edge of the timeout.
 * @param {number} [options.maxWait]
 *  The maximum time `func` is allowed to be delayed before it's invoked.
 * @param {boolean} [options.trailing=true]
 *  Specify invoking on the trailing edge of the timeout.
 * @returns {Function} Returns the new debounced function.
 * @example
 *
 * // Avoid costly calculations while the window size is in flux.
 * jQuery(window).on('resize', debounce(calculateLayout, 150))
 *
 * // Invoke `sendMail` when clicked, debouncing subsequent calls.
 * jQuery(element).on('click', debounce(sendMail, 300, {
 *   'leading': true,
 *   'trailing': false
 * }))
 *
 * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
 * const debounced = debounce(batchLog, 250, { 'maxWait': 1000 })
 * const source = new EventSource('/stream')
 * jQuery(source).on('message', debounced)
 *
 * // Cancel the trailing debounced invocation.
 * jQuery(window).on('popstate', debounced.cancel)
 *
 * // Check for pending invocations.
 * const status = debounced.pending() ? "Pending..." : "Ready"
 */
export function debounce(func, wait, options) {
    let lastArgs,
        lastThis,
        maxWait,
        result,
        timerId,
        lastCallTime

    let lastInvokeTime = 0
    let leading = false
    let maxing = false
    let trailing = true

    // Bypass `requestAnimationFrame` by explicitly setting `wait=0`.
    // const useRAF = (!wait && wait !== 0 && typeof root.requestAnimationFrame === 'function')
    /** 注释RAF检测，对整体函数执行无明显影响 **/

    if (typeof func !== 'function') {
        throw new TypeError('Expected a function')
    }
    wait = +wait || 0
    if (isObject(options)) {
        leading = !!options.leading
        maxing = 'maxWait' in options
        maxWait = maxing ? Math.max(+options.maxWait || 0, wait) : maxWait
        trailing = 'trailing' in options ? !!options.trailing : trailing
    }

    function invokeFunc(time) {
        const args = lastArgs
        const thisArg = lastThis

        lastArgs = lastThis = undefined
        lastInvokeTime = time
        result = func.apply(thisArg, args)
        return result
    }

    function startTimer(pendingFunc, wait) {
        // if (useRAF) {
        //   root.cancelAnimationFrame(timerId)
        //   return root.requestAnimationFrame(pendingFunc)
        // }
        return setTimeout(pendingFunc, wait)
    }

    function cancelTimer(id) {
        // if (useRAF) {
        //   return root.cancelAnimationFrame(id)
        // }
        clearTimeout(id)
    }

    function leadingEdge(time) {
        // Reset any `maxWait` timer.
        lastInvokeTime = time
        // Start the timer for the trailing edge.
        timerId = startTimer(timerExpired, wait)
        // Invoke the leading edge.
        return leading ? invokeFunc(time) : result
    }

    function remainingWait(time) {
        const timeSinceLastCall = time - lastCallTime
        const timeSinceLastInvoke = time - lastInvokeTime
        const timeWaiting = wait - timeSinceLastCall

        return maxing
            ? Math.min(timeWaiting, maxWait - timeSinceLastInvoke)
            : timeWaiting
    }

    function shouldInvoke(time) {
        const timeSinceLastCall = time - lastCallTime
        const timeSinceLastInvoke = time - lastInvokeTime

        // Either this is the first call, activity has stopped and we're at the
        // trailing edge, the system time has gone backwards and we're treating
        // it as the trailing edge, or we've hit the `maxWait` limit.
        return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
            (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait))
    }

    function timerExpired() {
        const time = Date.now()
        if (shouldInvoke(time)) {
            return trailingEdge(time)
        }
        // Restart the timer.
        timerId = startTimer(timerExpired, remainingWait(time))
    }

    function trailingEdge(time) {
        timerId = undefined

        // Only invoke if we have `lastArgs` which means `func` has been
        // debounced at least once.
        if (trailing && lastArgs) {
            return invokeFunc(time)
        }
        lastArgs = lastThis = undefined
        return result
    }

    function cancel() {
        if (timerId !== undefined) {
            cancelTimer(timerId)
        }
        lastInvokeTime = 0
        lastArgs = lastCallTime = lastThis = timerId = undefined
    }

    function flush() {
        return timerId === undefined ? result : trailingEdge(Date.now())
    }

    function pending() {
        return timerId !== undefined
    }

    function debounced(...args) {
        const time = Date.now()
        const isInvoking = shouldInvoke(time)

        lastArgs = args
        lastThis = this
        lastCallTime = time

        if (isInvoking) {
            if (timerId === undefined) {
                return leadingEdge(lastCallTime)
            }
            if (maxing) {
                // Handle invocations in a tight loop.
                timerId = startTimer(timerExpired, wait)
                return invokeFunc(lastCallTime)
            }
        }
        if (timerId === undefined) {
            timerId = startTimer(timerExpired, wait)
        }
        return result
    }
    debounced.cancel = cancel
    debounced.flush = flush
    debounced.pending = pending
    return debounced
}


export function throttle (func, wait, options) {
    let leading = true
    let trailing = true
   
    if (typeof func !== 'function') {
      throw new TypeError('Expected a function')
    }
    if (isObject(options)) {
      leading = 'leading' in options ? !!options.leading : leading
      trailing = 'trailing' in options ? !!options.trailing : trailing
    }
    return debounce(func, wait, {
      leading,
      trailing,
      maxWait: wait
    })
}

/**
 * 创建上级父信息
 * @param crumbs 当前选择项的crumbs
 * @returns 
 */
export function settleParentsInfo(crumbs:{
groupId:string,
groupName
}[]){
    let groupsInfo={},parents:any=[],parentGroupIds:any=[]

     crumbs.map((c) => { 
        const {groupId,groupName}=c
        parents.push({groupId,groupName})
        parentGroupIds.push(groupId)
     })

     groupsInfo=Object.assign(groupsInfo,{parents,parentGroupIds,parentGroupStr:parentGroupIds.join(",")})

     return groupsInfo
}




